
#define boardW 30
#define boardH 16

void Panel_dispose(Window* self);
void Panel_draw(Window* self);
void Panel_mouseEvent(Window* self, int x, int y, int e);
void Panel_keyEvent(Window* self, int key, int chr);

class Panel : public Window{
  public:
  int downx;
  int downy;
  int down;
  void init(DynamicObject<Window>* self){
    Window::init(self);
    width = boardW * 16;
    height = boardH * 16;
    pDispose = Panel_dispose;
    pDraw = Panel_draw;
    pMouseEvent = Panel_mouseEvent;
    pKeyEvent = Panel_keyEvent;
  }
  void dispose(){
    Window::dispose();
  }
  void draw2(int x0, int x1, int y0, int y1);
  void draw();
  void mouseEvent(int x, int y, int e);
  void keyEvent(int key, int chr){
    Window::keyEvent(key, chr);
  }
};
void Panel_dispose(Window* self){
  ((Panel*)self)->dispose();
}
void Panel_draw(Window* self){
  ((Panel*)self)->draw();
}

void Panel_mouseEvent(Window* self, int x, int y, int e){
  ((Panel*)self)->mouseEvent(x, y, e);
}
void Panel_keyEvent(Window* self, int key, int chr){
  ((Panel*)self)->keyEvent(key, chr);
}




int getColorRGB(int r, int g, int b){
  int val = 0;
  val += (r >> 3) << (5 + 6);
  val += (g >> 2) << (5);
  val += (b >> 3);
  return val;
}

void FormMineSweeper_mouseEvent(Window* self, int x, int y, int e);
void FormMineSweeper_close(Window*);
void FormMineSweeper_dispose(Window* self);
void FormMineSweeper_draw(Window* self);
void onMineSweeperTimer(Timer* timer);
void Button_mine_reset(Button* button);
void Button_draw(Window* w);
void Button_mouseEvent(Window* w, int x, int y, int e);

//#define MINESWEEPER_RES 0x70000
//after tetris
#define MINESWEEPER_RES 16*20*20*2

class FormMineSweeper : public Form
{
  public:

  int autoLevel; //0 no open, 1 open empty, 2: ai

  int mineBoard[boardW*boardH];
  int status[boardW*boardH];//0x00:close  0x10:open  0x01:flag mark  0x02:question mark

  int isFailed;
  int isWined;

  int imgArr[10];
  int num[10];
  int ma[5];//flag, question mark, red mine, x mine, mine
  int mi[5];

  DynamicObject<void>* imgBuff;
  DynamicObject<Timer>* timer;
  DynamicObject<Panel>* panel;

  int time;
  int mineCount;

  int loadImg(int basex, int basey, int x, int y, int w, int h, short* block){
    int bx = (basex + x) / 2;
    int by = basey + y;
    int* src = (int*)block;
    int* p = (int*)(imgBuff->obj);
    for(int j=0;j<h;j++){
      int j_temp = j*w/2;
      int* addr;
      for(int i=0;i<w/2;i++){
        int x = bx + i;
        int y = by + j;
        addr = p+x+(y<<9);
        *addr = src[i+j_temp];//at 2Mbyte
      }
      flushCache(addr-512);
      flushCache(addr);
    }
  }

  void drawNum(int bx, int by, int val){
    
    int bcd = toBcd(val);
    drawImgWin(bx+12*2, by, 12, 23, num[(bcd>>0)&0x0F], (int)(imgBuff->obj)>>11);
    drawImgWin(bx+12*1, by, 12, 23, num[(bcd>>4)&0x0F], (int)(imgBuff->obj)>>11);
    drawImgWin(bx+12*0, by, 12, 23, num[(bcd>>8)&0x0F], (int)(imgBuff->obj)>>11);
  }

  int inRange(int x, int y){
    if(x>=0 && x<boardW && y>=0 && y<boardH){
      return 1;
    }else{
      return 0;
    }
  }

  int getMine(int x, int y){
    if(inRange(x, y)){
      return mineBoard[x+y*boardW];
    }else{
      return 0;
    }
  }

  int getCount(int x, int y){
    int cnt = 0;
    cnt = cnt + getMine((x+1),(y+1));
    cnt = cnt + getMine((x  ),(y+1));
    cnt = cnt + getMine((x-1),(y+1));
    cnt = cnt + getMine((x+1),(y  ));
    cnt = cnt + getMine((x  ),(y  ));
    cnt = cnt + getMine((x-1),(y  ));
    cnt = cnt + getMine((x+1),(y-1));
    cnt = cnt + getMine((x  ),(y-1));
    cnt = cnt + getMine((x-1),(y-1));
    return cnt;
  }

  inline int getStatus(int x, int y){
    return status[x+y*boardW];
  }
  void setStatus(int x, int y, int val){
    status[x+y*boardW] = val;
  }



  int isWin(){
    int notyet=0;
    
    for(int j=0;j<boardH;j++){
      for(int i=0;i<boardW;i++){
        if(getMine(i, j)==0){
          if(getStatus(i, j)==0x10){//0x00:close  0x10:open  0x01:flag mark  0x02:question mark
          }else{
            notyet = 1;
          }
        }
      }
    }
    
    if(!notyet){
      return 1;
    }else{
      return 0;
    }
  }

  void open(int x, int y){
    if(inRange(x, y)){
      if(getStatus(x, y)==0){//0x00:close  0x10:open  0x01:flag mark  0x02:question mark
        setStatus(x, y, 0x10);
        if(isWin()){
          isWined = 1;
        }else{
          if(getMine(x, y)){
            isFailed = 1;
            this->needRefresh = 1;
          }else{
            int cnt = getCount(x, y);
            panel->obj->drawImgWin(x*16, y*16, 16, 16, imgArr[cnt], (int)(imgBuff->obj)>>11);//open
            if(cnt==0){
              open(x+1, y+1);
              open(x  , y+1);
              open(x-1, y+1);
              open(x+1, y  );
              //open(x  , y  );
              open(x-1, y  );
              open(x+1, y-1);
              open(x  , y-1);
              open(x-1, y-1);
            }
          }
        }
        //AI
      }
    }
  }

  void draw(){
    Form::draw();
    paintWin(0+502-3 + 2,        0 + 24,     3,   322, getColorRGB(255, 255, 255), 0);
    paintWin(      0 + 2,  0+322-3 + 24,   502,     3, getColorRGB(255, 255, 255), 0);
    paintWin(      0 + 2,        0 + 24,   502,     3, getColorRGB(255, 255, 255), 0);
    paintWin(      0 + 2,        0 + 24,     3,   322, getColorRGB(255, 255, 255), 0);
    paintWin(      3 + 2,        3 + 24, 502-6, 322-6, getColorRGB(192, 192, 192), 0);
    paintWin(      9 + 2,        9 + 24,   486,     2, getColorRGB(128, 128, 128), 0);
    paintWin(      9 + 2,        9 + 24,     2,    37, getColorRGB(128, 128, 128), 0);
    paintWin(9+486-2 + 2,        9 + 24,     2,    37, getColorRGB(255, 255, 255), 0);
    paintWin(      9 + 2,   9+37-2 + 24,   486,     2, getColorRGB(255, 255, 255), 0);
    paintWin(      9 + 2,       53 + 24,   485,     2, getColorRGB(128, 128, 128), 0);
    paintWin(      9 + 2,       53 + 24,     2,   261, getColorRGB(128, 128, 128), 0);
    paintWin(9+485-2 + 2,       53 + 24,     2,   261, getColorRGB(255, 255, 255), 0);
    paintWin(      9 + 2, 53+261-2 + 24,   485,     2, getColorRGB(255, 255, 255), 0);

    
    drawNum(448, 16+22, time);
    drawNum(18, 16+22, mineCount);

  }
    
  void reset(){
    int total = boardW*boardH;
    isFailed = 0;
    isWined = 0;
    time = 123;
    for(int i=0;i<total;i++){
      mineBoard[i] = 0;
    }
    for(int i=0;i<mineCount;i++){
      mineBoard[i] = 1;
    }
    
    for(int i=0;i<total;i++){
      int pos = i+rnd(total-i);
      int tmp = mineBoard[pos];
      mineBoard[pos] = mineBoard[i];
      mineBoard[i] = tmp;
    }
    
    for(int i=0;i<total;i++){
      status[i] = 0;
    }
    this->needRefresh = 1;
  }

  int rnd(int max){
    int v = ::rnd() & 0xFFFF;
    v = v * max;
    v = v >> 16;
    return v;
  }

  void onclick(int x, int y, int b1, int b2){
  }

  void onTimer() {
    if(isFailed || isWined)return;
    time++;
    drawNum(448, 16+22, time);
  }

  void init(DynamicObject<Window>* self){
    Form::init(self);
    mineCount = 20;
    time = 567;

    {
      DynamicObject<Button>* button = DynamicMem::inst->dMalloc<Button>("button20");
      Button* b = button->obj;
      b->init((DynamicObject<Window>*)button);
      b->id=1;
      b->pos_x = 100; b->pos_y = 16+22; b->width = 26;
      b->text="20";
      b->pOnClick = Button_mine_reset;
      addChild((DynamicObject<Window>*)button);
    }
    {
      DynamicObject<Button>* button = DynamicMem::inst->dMalloc<Button>("button50");
      Button* b = button->obj;
      b->init((DynamicObject<Window>*)button);
      b->id=2;
      b->pos_x = 140; b->pos_y = 16+22;  b->width = 26;
      b->text="50";
      b->pOnClick = Button_mine_reset;
      addChild((DynamicObject<Window>*)button);
    }
    {
      DynamicObject<Button>* button = DynamicMem::inst->dMalloc<Button>("button99");
      Button* b = button->obj;
      b->init((DynamicObject<Window>*)button);
      b->id=3;
      b->pos_x = 180; b->pos_y = 16+22;  b->width = 26;
      b->text="99";
      b->pOnClick = Button_mine_reset;
      addChild((DynamicObject<Window>*)button);
    }

    {
      DynamicObject<Button>* button = DynamicMem::inst->dMalloc<Button>("buttonCenter");
      Button* b = button->obj;
      b->init((DynamicObject<Window>*)button);
      b->id=4;
      b->pos_x = 240; b->pos_y = 16+22;  b->width = 25;  b->height = 25;
      b->pDraw = Button_draw;
      b->pMouseEvent = Button_mouseEvent;
      b->pOnClick = Button_mine_reset;
      addChild((DynamicObject<Window>*)button);
    }

    width = 509;
    height = 351;
    pos_x = 50;
    pos_y = 20;
    //b->text="timer";
    color = getRGB565(0x0000000);
    title="minesweeper";
    pOnClose = FormMineSweeper_close;
    pDispose = FormMineSweeper_dispose;
    pDraw = FormMineSweeper_draw;

    pMouseEvent = FormMineSweeper_mouseEvent;
    
    imgBuff = DynamicMem::inst->dMalloc<void>(2048*(26+2), 2048, "imgBuff", null);//  int temp = (int)malloc(2048*(26+2));//26line buff

    int res = (int)__getRes__();
    int posx = 0;
    int index = 0;
    for(int i=0;i<10;i++){
      imgArr[i] = posx;
      loadImg(posx, 0, 0, 0, 16, 16, (short*)(res + MINESWEEPER_RES + index));index+=16*16*2;posx+=16;
    }
    for(int i=0;i<5;i++){
      ma[i] = posx;
      loadImg(posx, 0, 0, 0, 16, 16, (short*)(res + MINESWEEPER_RES + index));index+=16*16*2;posx+=16;
    }
    for(int i=0;i<10;i++){
      num[i] = posx;
      loadImg(posx, 0, 0, 0, 12, 23, (short*)(res + MINESWEEPER_RES + index));index+=12*23*2;posx+=12;
    }
    for(int i=0;i<5;i++){
      mi[i] = posx;
      loadImg(posx, 0, 0, 0, 26, 26, (short*)(res + MINESWEEPER_RES + index));index+=26*26*2;posx+=26;
    }

    {
      timer = DynamicMem::inst->dMalloc<Timer>("timer");
      timer->obj->interval = 1000;//ms
      timer->obj->nextTick = 0;//ms
      timer->obj->pOnTime = onMineSweeperTimer;
      ::windows->obj->addTimer(timer);
    }
    
    {
      DynamicObject<Panel>* _do = DynamicMem::inst->dMalloc<Panel>("panel");
      Panel* obj = _do->obj;
      obj->init((DynamicObject<Window>*)_do);
      obj->id = 105;
      obj->pos_x = 13;
      obj->pos_y = 57+22;
      addChild((DynamicObject<Window>*)_do);
      panel = _do;
    }
    
    reset();
  }

  void dispose(){
    Form::dispose();
  }
};
void FormMineSweeper_close(Window* window){
  windows->obj->removeTimer(formMineSweeper->obj->timer);
  windows->obj->removeWindow((DynamicObject<Window>*)formMineSweeper);
  windows->obj->refresh();
  formMineSweeper=0;

}

void FormMineSweeper_dispose(Window* self){
  DynamicMem::inst->dFree(((FormMineSweeper*)self)->imgBuff);
  DynamicMem::inst->dFree(((FormMineSweeper*)self)->timer);
  ((FormMineSweeper*)self)->dispose();
}

void FormMineSweeper_draw(Window* self){
  ((FormMineSweeper*)self)->draw();
}

void FormMineSweeper_mouseEvent(Window* self, int x, int y, int e){
  ((FormMineSweeper*)self)->mouseEvent(x, y, e);
}

void onMineSweeperTimer(Timer* timer) {
  formMineSweeper->obj->onTimer();
}

void Button_mine_reset(Button* button){
  if(button->id==1){
    formMineSweeper->obj->mineCount = 20;
  }else if(button->id==2){
    formMineSweeper->obj->mineCount = 50;
  }else if(button->id==3){
    formMineSweeper->obj->mineCount = 99;
  }
  formMineSweeper->obj->reset();
}

void Button_draw(Window* w){
  w->Window::draw();

  FormMineSweeper* f = formMineSweeper->obj;
  
  int _imgBuff = (int)(f->imgBuff->obj)>>11;
  
  if(f->isWined){
    w->drawImgWin(2, 2, 23, 23, f->mi[2]+3, _imgBuff+3);
  }else if(f->isFailed){
    w->drawImgWin(2, 2, 23, 23, f->mi[1]+3, _imgBuff+3);
  }else{
    w->drawImgWin(2, 2, 23, 23, f->mi[0]+3, _imgBuff+3);
  }
}

void Button_mouseEvent(Window* w, int x, int y, int e){
  short btn = e>>16;
  short state = e&0xFFFF;
  if(state){
    int shade = getColorRGB(255, 255, 255);
    int bright = getColorRGB(128, 128, 128);
    w->paintWin(0               , 0                ,        2, w->height, (shade<<16) | bright, 0);
    w->paintWin(0               , 0                , w->width,         2, (shade<<16) | bright, 0);
    w->paintWin(0 + w->width - 2, 0                ,        2, w->height,(bright<<16) |  shade, 0);
    w->paintWin(0               , 0 + w->height - 2, w->width,         2,(bright<<16) |  shade, 0);
  }else{
    int bright = getColorRGB(255, 255, 255);
    int shade = getColorRGB(128, 128, 128);
    w->paintWin(0               , 0                ,        2, w->height, (shade<<16) | bright, 0);
    w->paintWin(0               , 0                , w->width,         2, (shade<<16) | bright, 0);
    w->paintWin(0 + w->width - 2, 0                ,        2, w->height,(bright<<16) |  shade, 0);
    w->paintWin(0               , 0 + w->height - 2, w->width,         2,(bright<<16) |  shade, 0);
    if(x>0 && x<w->width && y>0 && y<w->height){
      Button* b = (Button*)w;
      if(b->pOnClick){
        b->pOnClick(b);
      }
    }
  }
  w->Window::mouseEvent(x, y, e);
}

void Panel::draw2(int x0, int x1, int y0, int y1){
  FormMineSweeper* _formMineSweeper = formMineSweeper->obj;

  int isFinish = _formMineSweeper->isFailed || _formMineSweeper->isWined;

  int _imgBuff = (int)(_formMineSweeper->imgBuff->obj)>>11;
  
  for(int j=y0;j<y1;j++){
    for(int i=x0;i<x1;i++){
      int status = _formMineSweeper->getStatus(i, j);
      int mine = _formMineSweeper->getMine(i, j);
      switch(status){//      
      case 0x00://0x00:close
        drawImgWin(i*16, j*16, 16, 16, _formMineSweeper->imgArr[9], _imgBuff);//close
        break;
      case 0x10://0x10:open
        if(mine){
          drawImgWin(i*16, j*16, 16, 16, _formMineSweeper->ma[4], _imgBuff);//mine
        }else{
          int cnt = _formMineSweeper->getCount(i, j);
          drawImgWin(i*16, j*16, 16, 16, _formMineSweeper->imgArr[cnt], _imgBuff);//open
        }
        break;
      case 0x01://0x01:flag mark
        if(isFinish){
          if(mine){
            drawImgWin(i*16, j*16, 16, 16, _formMineSweeper->ma[4], _imgBuff);//mine
          }else{
            drawImgWin(i*16, j*16, 16, 16, _formMineSweeper->ma[3], _imgBuff);//x mine
          }
        }else{
          drawImgWin(i*16, j*16, 16, 16, _formMineSweeper->ma[0], _imgBuff);//flag
        }
        break;
      case 0x02://0x02:question mark
        if(isFinish){
          if(mine){
            drawImgWin(i*16, j*16, 16, 16, _formMineSweeper->ma[4], _imgBuff);//mine
          }else{
            drawImgWin(i*16, j*16, 16, 16, _formMineSweeper->imgArr[0], _imgBuff);//open
          }
        }else{
          drawImgWin(i*16, j*16, 16, 16, _formMineSweeper->ma[1], _imgBuff);//flag
        }
        break;
      }
    }
  }
}

void Panel::draw(){
  Window::draw();
  
  draw2(0, boardW, 0, boardH);

}
void Panel::mouseEvent(int x, int y, int e){
  FormMineSweeper* _formMineSweeper = formMineSweeper->obj;
  if(_formMineSweeper->isFailed || _formMineSweeper->isWined)return;
  short btn = e>>16;
  short state = e&0xFFFF;
  
  int _imgBuff = (int)(_formMineSweeper->imgBuff->obj)>>11;

  x = x / 16;
  y = y / 16;
  if(btn==1){
    if(state){
      downx = x;
      downy = y;
      down = 1;
      //if(_formMineSweeper->getStatus(x, y)==0){
      //  drawImgWin(x*16, y*16, 16, 16, _formMineSweeper->imgArr[0], _imgBuff);//
      //}
    }else{
      
      if(down==1){
        _formMineSweeper->open(downx, downy);
        down = 0;
      }
    }
  }
  if(state && (btn==2)){
    int status = _formMineSweeper->getStatus(x, y);//0x00:close  0x10:open  0x01:flag mark  0x02:question mark
    if(status==0){
      _formMineSweeper->setStatus(x, y, 0x01);
      _formMineSweeper->mineCount--;
    }
    if(status==0x01){
      _formMineSweeper->setStatus(x, y, 0x02);
      _formMineSweeper->mineCount++;
    }
    if(status==0x02){
      _formMineSweeper->setStatus(x, y, 0x00);
    }
  }
  draw2(x, x + 1, y, y + 1);
}

